from flask import request
from flask_restful import Resource, abort, fields, marshal_with, marshal, reqparse

from App.models import Goods

# 定义序列化格式
# 单个对象格式
goods_fields = {
    "id": fields.Integer,
    "name": fields.String(attribute='g_name'),  # attribute属性对应数据列名，前面自定义名可给前端换一个名字name,
    "g_price": fields.Float,
    "uri": fields.Url("single_goods", absolute=True),  # endpoint , absolute 绝对路径
}

# 单个对象+返回其他信息status、msg格式
single_goods_fields = {
    "data": fields.Nested(goods_fields),
    "status": fields.Integer,
    "msg": fields.String
}
# 多个对象+返回其他信息status、msg格式
multi_goods_fields = {
    "status": fields.Integer,
    "msg": fields.String,
    "desc": fields.String(default='name is g_name ,success'),  # "desc" 名后台数据列无，默认返回空，default 可指定默认值
    "data": fields.List(fields.Nested(goods_fields)),
}

# 定义 输入参数的 验证前端提供的数据的规则
parser = reqparse.RequestParser()  # 定义 reqparse 对象
parser.add_argument("g_name", type=str, required=True, help='请输入名称')  # reqparse 对象中添加字段，并制定验证规则
parser.add_argument("g_price", type=int, required=True, help='请输入单价')


# 单个对象请求逻辑类
class GoodsResource(Resource):

    @marshal_with(single_goods_fields)
    def get(self, id):
        goods = Goods.query.get(id)
        data = {
            "status": 200,
            "msg": "ok",
            "data": goods
        }
        return data

    @marshal_with(single_goods_fields)  # 使用装饰器 序列化返回值
    def post(self, id):
        # g_name = request.form.get('g_name')
        # g_price = request.form.get('g_price')
        args = parser.parse_args()  # 得到字典格式 根据上面reqparse定义， 可验证前端提供的数据
        g_name = args.get('g_name')  # 从 reqparse 对象中获得 字段
        g_price = args.get('g_price')
        goods = Goods()
        goods.g_name = g_name
        goods.g_price = g_price

        data = {
            "msg": 'create success',
            "status": 201,
            # "data": marshal(goods, goods_fields)  # marshal() 实现单个对象序列化
            "data": goods  # 改由头部装饰器 最后 序列化@marshal_with()
        }

        if not goods.save():  # Model类封装定义个方法简化save()
            abort(400)

        # return data, 201, {'Etag': 'some-opaque-string'} # 被上面marshal后都对象不能使用
        return data  # 改由头部装饰器序列化@marshal_with()

    @marshal_with(single_goods_fields)  # 无 goods对象返回时，不能使用
    def delete(self, id):
        goods = Goods.query.get(id)
        if not goods:
            abort(404, status='fail', message="id=%d 未找到" % id)
        if not goods.delete():  # delete() 为类封装方法
            abort(404)
        data = {
            "msg": "delete success",
            "status": 204,
            "data": goods
        }
        return data

    def put(self, id):
        """要求前端提供的一定是一个完整的资源对象,缺了的那些字段会被清空"""
        goods = Goods.query.get(id)
        if not goods:
            abort(404)
        g_price = request.form.get('g_price')
        g_name = request.form.get('g_name')
        goods.g_name = g_name
        goods.g_price = g_price
        if not goods.save():
            abort(404)
        data = {
            "msg": "put success",
            "status": 201,
            "data": goods,
        }
        return marshal(data, single_goods_fields), 201  # 这里marshall可以返回状态代码、头信息

    @marshal_with(single_goods_fields)
    def patch(self, id):
        """ 用来更新局部资源 ,请求是一个局部更新，后端仅更新接收到的字段"""
        goods = Goods.query.get(id)
        if not goods:
            abort(404)
        g_price = request.form.get('g_price')
        g_name = request.form.get('g_name')
        goods.g_name = g_name or goods.g_name
        goods.g_price = g_price or goods.g_price
        if not goods.save():
            abort(404)
        data = {
            "msg": "patch success",
            "status": 201,
            "data": goods,
        }
        return data


# 多个对象请求逻辑
class GoodsesResource(Resource):

    @marshal_with(multi_goods_fields)  # 使用装饰器 序列化返回值
    def get(self):
        goodses = Goods.query.all()
        data = {
            "msg": 'create success',
            "status": 201,
            "data": goodses
        }
        return data
